/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*********************************************************************************************
    *   Filename        : poweroff_reboot.c

    *   Description     :

    *   Author          : Bingquan

    *   Email           : bingquan_cai@zh-jieli.com

    *   Last modifiled  : 2018-07-09 16:24

    *   Copyright:(c)JIELI  2011-2017  @ , All Rights Reserved.
*********************************************************************************************/
#include "asm/cpu.h"
#include "power_hw.h"
#include "asm/power_interface.h"
#include "gpio.h"
#include "asm/hwi.h"

#define LOG_TAG             "[REBOOT]"
#define LOG_ERROR_ENABLE
#define LOG_INFO_ENABLE
#define LOG_DUMP_ENABLE
#include "debug.h"

#define TRIGGER         __asm__ volatile ("trigger")

#define IRQ_FUNCTION_PROTOTYPE(idx, hdl) \
    __attribute__((interrupt("")))

#define IRQ_REGISTER(idx, hdl, prio) \
    request_irq(idx, prio, hdl, 0)

#pragma str_literal_override("reboot.rodata")

void delay(u32 d);

void _reboot_start(void);

AT_NON_VOLATILE_RAM_CODE
void maskrom_api_init(void)
{
    if (maskrom_version() == 0x20180701) {
    }
}

/* ***************************************************************** */
AT_NON_VOLATILE_RAM_CODE
void switch_to_lrc()
{
    JL_CLOCK->CLK_CON0 &= ~BIT(1);
    JL_CLOCK->CLK_CON0 &= ~BIT(8); // sys switch rc clk 250k
}

#define pll_con1_init                           \
    (0 << 30) /* CKDAC OE     1bit RW  */ | \
    (1 << 28) /* D4P5 OE      1bit RW  */ | \
    (1 << 27) /* D3P5 OE      1bit RW  */ | \
    (1 << 26) /* D2P5 OE      1bit RW  */ | \
    (1 << 25) /* D1P5 OE      1bit RW  */ | \
    (1 << 24) /* D1 OE        1bit RW  */ | \
    (1 << 22) /* LDO 12D      2bit RW  */ | \
    (1 << 20) /* LDO 12A      2bit RW  */ | \
    (0 << 19) /* 200K EN      1bit RW  */ | \
    (0 << 18) /* TEST EN      1bit RW  */ | \
    (0 << 16) /* TEST SEL     2bit RW  */ | \
    (0 << 15) /* LDO BYPASS   1bit RW  */ | \
    (3 << 12) /* IVCOS        3bit RW  */ | \
    ((480 / 12 - 2) << 0) /* DS          12bit RW  */

#define pll_con_init                            \
    (2 << 26) /* LPFR2S       2bit RW  */ | \
    (1 << 24) /* ICPS         2bit RW  */ | \
    (1 << 22) /* PFDS         2bit RW  */ | \
    (0 << 10) /* TSCK 480M OE 1bit RW  */ | \
    (pll_ds_en << 8) /* REF DS EN    2bit RW  */ | \
    (0 << 7) /* REFSEL       1bit RW  */ | \
    (0 << 2) /* REFDS        5bit RW  */ | \
    (0 << 1) /* RESET EN     1bit RW  */ | \
    (0 << 0) /* EN           1bit RW  */

AT_NON_VOLATILE_RAM_CODE
static void pll_init_lite(void)
{
    u32 osc_freq;

    osc_freq = 12000000L;
    // pll init
    JL_CLOCK->CLK_CON0 &= ~(3 << 2); // clk mux0 sel rc

    u8 pll_ds_en = (osc_freq == 24000000) ? 0b10 : 0b01;
    JL_CLOCK->PLL_CON = pll_con_init;
    JL_CLOCK->PLL_CON1 = pll_con1_init;

    JL_CLOCK->PLL_CON |= BIT(0); // EN
    switch_to_lrc(); // sys switch rc clk 250k
    // wait 10 us
    delay(10);
    JL_CLOCK->PLL_CON |= BIT(1); // RST
    // wait 100 us
    delay(100);

    // sys clk
    JL_CLOCK->CLK_CON2 &= ~(0x3f);    // pll_192M
    JL_CLOCK->CLK_CON2 |= BIT(5);     // pll div4 sys_clk = 192/4=48M

    SFR(JL_CLOCK->CLK_CON0, 6, 2, 0x3);
    JL_CLOCK->CLK_CON0 |= BIT(8);     // sys switch pll clk 48M
}

/* ***************************************************************** */
static int ticks;

#define MAX_TIME_CNT    0x7fff
#define MIN_TIME_CNT    0x100

#define MAX_TIMER_PERIOD_MS        (10000>>1)

AT_NON_VOLATILE_RAM_CODE
static void __timer0_isr1(void)
{
    struct timer_t *p;

    JL_TIMER0->CON |= BIT(14);

    ticks++;

    if (ticks == 500) {
        ticks = 0;
        puts("X");
    }
}

static const u32 timer_div[] = {
    1,           /* 0000 */
    4,           /* 0001 */
    16,          /* 0010 */
    64,          /* 0011 */
    2,           /* 0100 */
    8,           /* 0101 */
    32,          /* 0110 */
    128,         /* 0111 */
    256,         /* 1000 */
    4 * 256,     /* 1001 */
    16 * 256,    /* 1010 */
    64 * 256,    /* 1011 */
    2 * 256,     /* 1100 */
    8 * 256,     /* 1101 */
    32 * 256,    /* 1110 */
    128 * 256,   /* 1111 */
};

AT_NON_VOLATILE_RAM_CODE
static void __periodic_timer0_init(void)
{
    u32 clk;
    u32 prd_cnt;
    u8 index;

    clk = 24000000L;

    clk /= 1000;
    clk *= 2;

    for (index = 0; index < (sizeof(timer_div) / sizeof(timer_div[0])); index++) {
        prd_cnt = clk / timer_div[index];
        if (prd_cnt > MIN_TIME_CNT && prd_cnt < MAX_TIME_CNT) {
            break;
        }
    }

    IRQ_REGISTER(IRQ_TIME0_IDX, __timer0_isr1, 1);

    JL_TIMER0->CNT = 0;
    JL_TIMER0->PRD = prd_cnt;
    JL_TIMER0->CON = (index << 4) | BIT(3) | BIT(0);
}

const char Date[] = "\r\n*********br18 reboot "__DATE__ " "__TIME__ "******\r\n";

typedef struct __FAST_SFC_SET {
    u32 flash_size;
    u32 flash_base;;
    u32 cfg_file_addr;
    u32 app_file_addr;
    u32 spi_mode;
    u32 flash_mode;
    u32 spi_div;
    u32 spi_run_mode;
    u32 pll_info;
    u32 cfg_zone_addr;
    u16 spi_port;
    u16 dec_key;
} FAST_SFC_SET;

#define RAM1_SAVE_VALUES_ADDR   (RAM1_ENTRY_ADDR - sizeof(FAST_SFC_SET)) // (RAM1_END - ROM_USE_SIZE - SFC_INFO_SIZE)

AT_NON_VOLATILE_RAM_CODE
static void nv_putchar(char c)
{
}

AT_NON_VOLATILE_RAM_CODE
void poweroff_post(void)
{
    int cnt;

    power_set_proweroff();

    pll_init_lite();

    while (1) {
        IO_DEBUG_TOGGLE(B, 0);
        delay(500000);        // for bt osc
    }
}

#define nv_p33_cs_h    JL_P33->SPI_CON  |= BIT(0)

#define nv_p33_cs_l    JL_P33->SPI_CON  &= ~BIT(0)

static u8 nv_p33_buf(u8 buf)
{
    JL_P33->SPI_DAT = buf;
    JL_P33->SPI_CON |= BIT(4);

    while (JL_P33->SPI_CON & BIT(1)) {
    }
    return JL_P33->SPI_DAT;
}

static void nv_p33_and_1byte(u16 addr, u8 data0)
{
    nv_p33_cs_h;
    nv_p33_buf((P33_AND << 5) | (u8)((addr & 0x300) >> 8));               // rd    //adr 3
    nv_p33_buf((u8)(addr & 0xff));       // wr    //adr 3
    nv_p33_buf(data0);
    nv_p33_cs_l;
}

AT_NON_VOLATILE_RAM_CODE
void nv_watchdog_close(void)
{
    nv_p33_and_1byte(P3_WDT_CON, ~BIT(4));
}

AT_NON_VOLATILE_RAM_CODE
void poweroff_pre(void)
{
    p33_tx_1byte(P3_PMU_CON0, PD_CON0_POWEROFF);

    int sfr, sfr1;

    p33_tx_1byte(P3_SOFT_FLAT, SOFT_FLAG_BT_OSC_ENABLE | SOFT_FLAG_SWITCH_OSC_CLK);
    p33_tx_1byte(P3_SOFT_FLAT1, SOFT_FLAG_POWEROFF_ENABLE);

    log_info("SOFT flag : 0x%x / 0x%x", p33_rx_1byte(P3_SOFT_FLAT), p33_rx_1byte(P3_SOFT_FLAT1));

    log_info("ROM ver. 0x%x", maskrom_version());

    maskrom_api_init();

    nvram_jumpaddr_set((u32)_reboot_start);

    nvram_signature_set();
}

IRQ_FUNCTION_PROTOTYPE(IRQ_EXCEPTION_IDX, exception_irq_handler)
AT_NON_VOLATILE_RAM_CODE
static void exception_isr(void)
{
    u32 rets, reti;
    u32 sp, usp, ssp;

    TRIGGER;

    // must be first.
    __asm__ volatile("%0 = rets":"=r"(rets));
    __asm__ volatile("%0 = reti":"=r"(reti));
    __asm__ volatile("%0 = sp" : "=r"(sp));
    __asm__ volatile("%0 = usp" : "=r"(usp));
    __asm__ volatile("%0 = ssp" : "=r"(ssp));

    log_error("***************************");
    log_error("RETS = %08x \r\n", rets);
    log_error("SP = %08x \r\n", sp);
    log_error("USP = %08x \r\n", usp);
    log_error("SSP = %08x \r\n", ssp);
    log_error("***************************");

    while (1) {
    }
}
